﻿/* Write a program that fills and prints a matrix of size (n, n) as shown below: (examples for n = 4)
 * a) 1 5  9 13             b) 1 8  9 16
 * 2 6 10 14                   2 7 10 15
 * 3 7 11 15                   3 6 11 14
 * 4 8 12 16                   4 5 12 13
 * 
 * c) 7 11 14 16               1 12 11 10
 *    4  8 12 15               2 13 16 9
 *    2  5 9 13                3 14 15 8
 *    1  3 6 10                4  5  6 7
 */




using System;

class MatrixNN
{
    static void Main()
    {
        //get matrix size from user input
        uint N;

            //check for invalid input
        bool iProvided = false;
        do
        {
            if (iProvided == true) Console.WriteLine("Error: invalid input! Please enter a positive integer.\r\n\t* Valid input: N > 0");
            else iProvided = true;
            Console.Write("Enter N: ");
        } while (UInt32.TryParse(Console.ReadLine(), out N) == false || N < 1);
        //Console.WriteLine(N);

        //Fill Matrix type a)
        int[,] matrix = new int[N, N];
        matrix = FillMatrixTypeA(matrix, (int)N);

        //Fill Matrix type b)
        int[,] matrix_b = new int[N, N];
        matrix_b = FillMatrixTypeB(matrix_b, (int)N);

        //Fill Matrix type c)
        int[,] matrix_c = new int[N, N];
        matrix_c = FillMatrixTypeC(matrix_c, (int)N);

        //Fill Matrix type c)
        int[,] matrix_d = new int[N, N];
        matrix_d = FillMatrixTypeD(matrix_d, (int)N);

        Console.WriteLine("Matrix A: ");
        PrintMatrix(matrix);

        Console.WriteLine("Matrix B: ");
        PrintMatrix(matrix_b);

        Console.WriteLine("Matrix C: ");
        PrintMatrix(matrix_c);

        Console.WriteLine("Matrix D: ");
        PrintMatrix(matrix_d);


    }

    private static int[,] FillMatrixTypeD(int[,] matrix, int mSize)
    {
        int pointer = 0, x = 0, y = 0, dy = 0, dx = 1;

        for (int currentValue = 1; currentValue <= Math.Pow(mSize, 2); currentValue++)
        {
            matrix[x, y] = currentValue;

            if (CheckCell(mSize, x + dx, y + dy) == false || matrix[x + dx, y + dy] > 0)
            {
                pointer += 1;
                pointer %= 4;
                
                if (pointer == 0)
                {
                    dy = 0;
                    dx = 1;
                }
                else if (pointer == 1)
                {
                    dy = 1;
                    dx = 0;
                }
                else if (pointer == 2)
                {
                    dy = 0;
                    dx = -1;
                }
                else if (pointer == 3)
                {
                    dy = -1;
                    dx = 0;
                }
                else
                {
                    Console.WriteLine("Invalid direction value: {0}", pointer);
                }
            }

            x += dx;
            y += dy;
        }

        return matrix;
    }

    private static bool CheckCell(int mSize, int p1, int p2)
    {
        return p1 >= 0 && p1 < mSize && p2 >= 0 && p2 < mSize;
    }

    private static int[,] FillMatrixTypeC(int[,] matrix, int mSize)
    {
        int i = 1;
        //int TemporaryStack = mSize * 2 - 1;

        for (int row = 0; row < mSize; row++)
        {
            for (int col = 0; col <= row; col++)
            {
                matrix[mSize - row + col - 1, col] = i;
                i++;
            }
        }
        for (int row = mSize - 2; row >= 0; row--)
        {
            for (int col = row; col >= 0; col--)
            {
                matrix[row - col, mSize - col - 1] = i;
                i++;
            }
        }
        
        return matrix;
    }

    private static int[,] FillMatrixTypeB(int[,] matrix, int mSize)
    {
        int i = 1;
        int TemporaryStack = (int)mSize; //stack for the even columns
        for (int col = 0; col < mSize; col++)
        {
            //this is the key element here - we get the last element of the previous column, and
            //then summarize it with the size of the matrix (i.e. for a matrix 5x5, the last element would be 5,
            //then we add 5 to the size of the matrix, and we receive the top of the ODD columns, thus we can
            //easily iterate decreasing the column value. 

            //we store the TOP element of the ODD columns in this stack
            if (col > 0) TemporaryStack = matrix[mSize-1, col - 1]+mSize; 
            
            for (int row = 0; row < mSize; row++)
            {
                if (col == 0) matrix[row, col] = i;
                else if (col % 2 != 0)
                {
                    //we got an ODD column, assign its value and decrease our stack
                    matrix[row, col] = TemporaryStack;
                    TemporaryStack--;
                }
                else matrix[row, col] = i;

                i++;
            }
        }
        return matrix;
    }

    private static int[,] FillMatrixTypeA(int[,] matrix, int mSize)
    {
        int i = 1, stack = i;

        //fill matrix a)
        for (int row = 0; row < mSize; row++)
        {
            matrix[row, 0] = i; //fill all first columns in <row> with <i> (1, 2, 3, 4)
            stack = i; //generate a stack which we will use as an offset to calculate the value in each next column

            for (int col = 1; col < mSize; col++)
            {
                stack += (int)mSize; //calculate next column value and store it in the stack
                matrix[row, col] = stack; //assign to memory
            }
            i++;
        }
        return matrix;
    }

    private static void PrintMatrix(int[,] matrix)
    {
        for (int row = 0; row < matrix.GetLength(0); row++)
        {

            for (int col = 0; col < matrix.GetLength(1); col++) Console.Write("{0,-5}", matrix[row, col]);
            Console.WriteLine();
        }
        Console.WriteLine();
    }
}
